﻿using Redis读写分离.Common;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Redis读写分离
{
    public class RedisReadWriteManager : IDisposable
    {
        private readonly ConnectionMultiplexer _writeConnection;
        private readonly List<ConnectionMultiplexer> _readConnections = new List<ConnectionMultiplexer>();
        private readonly Random _random = new Random();
        private bool _disposed;

        public RedisReadWriteManager(Configuration config)
        {
            if (config == null) throw new ArgumentNullException(nameof(config));

            // 配置写连接(主节点)
            var writeConfig = ConfigurationOptions.Parse(config.MasterConnectionString);
            writeConfig.AbortOnConnectFail = false;
            writeConfig.ConnectTimeout = config.ConnectTimeout;
            writeConfig.SyncTimeout = config.SyncTimeout;
            _writeConnection = ConnectionMultiplexer.Connect(writeConfig);

            // 配置读连接(从节点)
            foreach (var slaveConnStr in config.SlaveConnectionStrings)
            {
                var readConfig = ConfigurationOptions.Parse(slaveConnStr);
                readConfig.AbortOnConnectFail = false;
                readConfig.ConnectTimeout = config.ConnectTimeout;
                readConfig.SyncTimeout = config.SyncTimeout;

                var readConnection = ConnectionMultiplexer.Connect(readConfig);
                _readConnections.Add(readConnection);

                // 注册连接失败事件
                readConnection.ConnectionFailed += (sender, args) =>
                {
                    Console.WriteLine($"读节点连接失败: {args.EndPoint}, 异常: {args.Exception?.Message}");
                };
            }
        }

        /// <summary>
        /// 获取写数据库连接(主节点)
        /// </summary>
        public ConnectionMultiplexer GetWriteConnection()
        {
            return _writeConnection;
        }

        /// <summary>
        /// 获取读数据库连接(从节点)
        /// </summary>
        public List<ConnectionMultiplexer> GetReadConnections()
        {
            return _readConnections.ToList();
        }

        /// <summary>
        /// 获取写数据库(主节点)
        /// </summary>
        public IDatabase GetWriteDatabase(int db = -1)
        {
            return _writeConnection.GetDatabase(db);
        }

        /// <summary>
        /// 获取读数据库(从节点，随机选择)
        /// </summary>
        public IDatabase GetReadDatabase(int db = -1)
        {
            if (_readConnections.Count == 0)
            {
                // 没有可用的读节点，降级到写节点
                return GetWriteDatabase(db);
            }

            // 随机选择一个读节点
            var index = _random.Next(_readConnections.Count);
            return _readConnections[index].GetDatabase(db);
        }

        /// <summary>
        /// 获取所有服务器状态
        /// </summary>
        public Dictionary<string, string> GetServerStatus()
        {
            var status = new Dictionary<string, string>();

            try
            {
                var writeServer = _writeConnection.GetServer(_writeConnection.GetEndPoints().First());
                status.Add("Master", $"{writeServer.EndPoint} - {writeServer.ServerType} - {(writeServer.IsConnected ? "Connected" : "Disconnected")}");
            }
            catch (Exception ex)
            {
                status.Add("Master", $"Error: {ex.Message}");
            }

            for (int i = 0; i < _readConnections.Count; i++)
            {
                try
                {
                    var readServer = _readConnections[i].GetServer(_readConnections[i].GetEndPoints().First());
                    status.Add($"Slave_{i}", $"{readServer.EndPoint} - {readServer.ServerType} - {(readServer.IsConnected ? "Connected" : "Disconnected")}");
                }
                catch (Exception ex)
                {
                    status.Add($"Slave_{i}", $"Error: {ex.Message}");
                }
            }

            return status;
        }

        public virtual void Dispose()
        {
            if (_disposed) return;

            _writeConnection?.Dispose();
            foreach (var conn in _readConnections)
            {
                conn?.Dispose();
            }
            _disposed = true;
        }
    }
}
